﻿#pragma once

#include <stdexcept>
#include <string>

namespace xy
{

namespace utils
{

// 默认情况下，value 为 false
template <typename T, typename T2> struct is_same
{
    const static bool value = false;
};

// 如果 T 和 T2 相同，则 value 为 true
template <typename T> struct is_same<T, T>
{
    const static bool value = true;
};

// 变量模板
template <typename T, typename T2> inline bool is_same_v = is_same<T, T2>::value;

// 原始模板，接收两个参数，什么也不做
template <bool B, class T = void> struct enable_if
{
};

// 特化版本，提供一个 type 类型
template <class T> struct enable_if<true, T>
{
    typedef T type;
};

// 判断 T 是否是一个类
template <typename T> class is_class
{
  private:
    typedef char One;
    typedef struct
    {
        char a[2];
    } Two;

    /**
     * @brief 如果 C 是类，则会调用此函数.
     *
     * @tparam C int C::* 是类成员指针，即使 C 没有整型成员，也可以声明这个类型，只是不能使用
     * @return One
     */
    template <typename C> static One test(int C::*);

    /**
     * @brief 如果 C 不是类，就会调用此函数.
     *
     * @tparam C
     * @param[in] ... 接收任意参数
     * @return Two
     */
    template <typename C> static Two test(...);

  public:
    // 获得返回值大小，One 对应 1 字节，Two 对应 2 字节
    enum
    {
        Yes = sizeof(is_class<T>::test<T>(0)) == 1
    };
    enum
    {
        No = !Yes
    };
};

// 实现细节
namespace details
{

template <typename UnnamedType> struct container
{
  private:
    /**
     * @brief std::declval<UnnamedType>() 获得一个 UnnamedType 类型的对象，如果 UnnamedType
     * 是可调用的对象，则匹配此模板函数，执行 UnnamedType()( std::declval<Param>() ).
     *
     * @tparam Params
     */
    template <typename... Params>
    constexpr auto test_validity(int /* unused */)
        -> decltype(std::declval<UnnamedType>()(std::declval<Params>()...), std::true_type())
    {
        return std::true_type();
    }

    /**
     * @brief 用于接收 UnnamedType 不是可调用对象的情况
     *
     * @tparam Params
     * @param[in] ...
     * @return constexpr std::false_type
     */
    template <typename... Params> constexpr std::false_type test_validity(...)
    {
        return std::false_type();
    }

  public:
    /**
     * @brief 重载使得 container 成为可调用对象。传入的 Param 是一个参数，作为 UnnamedType 调用的参数
     *
     * @tparam Params
     * @return constexpr auto
     */
    template <typename... Params> constexpr auto operator()(Params &&...)
    {
        return test_validity<Params...>(int());
    }
};

} // namespace details

/**
 * @brief is_valid 模板，传入一个 UnnamedType 类型，返回 container 是一个可调用对象.
 *
 * @tparam UnnamedType
 * @param[in] t
 * @return constexpr auto
 */
template <typename UnnamedType> constexpr inline auto is_valid(UnnamedType &&t)
{
    return details::container<UnnamedType>();
}

namespace details
{

/**
 * @brief 获得函数签名
 *
 * @tparam T
 * @tparam N
 * @return const char *
 */
template <class T, T N> inline const char *enum_name_static()
{
#if defined(_MSC_VER)
    return __FUNCSIG__;
#elif defined(__GNUC__) || defined(__clang__)
    return __PRETTY_FUNCTION__;
#else
#error "Unsupported compiler!"
#endif
}

// 编译期循环
template <int Beg, int End, class F, typename std::enable_if_t<Beg == End, int> = 0> void static_for(const F &func)
{
}

template <int Beg, int End, class F, typename std::enable_if_t<Beg != End, int> = 0> void static_for(const F &func)
{
    struct int_constant
    {
        enum
        {
            value = Beg
        };
    };
    func(int_constant());
    static_for<Beg + 1, End>(func);
}

} // namespace details

/**
 * @brief 获得枚举名称
 *
 * @tparam Beg
 * @tparam End
 * @tparam T
 * @param[in] n
 * @return std::string
 */
template <int Beg = 0, int End = 64, class T> inline std::string enum_name(T n)
{
    // 保存 enum_name_static 模板函数的信息
    std::string s;

    // 循环展开模板。Lambda 表达式中 auto i 相当于 T i
    details::static_for<Beg, End + 1>([&](auto i) {
        // 只有当 n 与整型转换后对应的枚举值相同时，获得对应模板函数的信息
        if (n == (T)i.value)
            s = details::enum_name_static<T, (T)i.value>();
    });

    // 如果没找到对应的整型，说明不存在对应枚举，直接返回数字字符串
    if (s.empty())
        return std::to_string((int)n);

    auto pos = s.find(",");
    pos += 1;
    auto pos2 = s.find_last_of('>', pos);

    // 裁剪函数信息，只保留枚举部分；如果还有 ::，将其也裁剪掉（例如 Color::Yellow -> Yellow）
    s = s.substr(pos, pos2 - pos);
    auto pos3 = s.find_last_of("::");
    auto pos4 = s.find_last_of(">");
    if (pos3 != s.npos)
        s = s.substr(pos3 + 1, pos4 - pos3 - 1);
    return s;
}

/**
 * @brief 从名称获得枚举
 *
 * @tparam T
 * @tparam Beg
 * @tparam End
 * @param[in] s
 * @return T
 */
template <class T, int Beg = 0, int End = 64> inline int enum_from_name(const std::string &s)
{
    for (int i = Beg; i < End; i++)
    {
        // 将枚举变为字符串比较
        if (s == enum_name((T)i))
            return (int)i;
    }
    throw std::out_of_range("Out of range");
}

} // namespace utils

} // namespace xy
